Migrate from the `term` crate to `termcolor`
authorAlex Crichton <alex@alexcrichton.com>
Thu, 8 Jun 2017 22:09:04 +0000 (15:09 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Tue, 13 Jun 2017 21:28:37 +0000 (14:28 -0700)
The API of `termcolor` fits what the system gives us much more nicely and should
be well battle-tested from ripgrep. Additionally we don't really need huge
terminfo parsers, that wasn't every really the intention of the color support
here.

20 files changed:
Cargo.lock
Cargo.toml
src/bin/cargo.rs
src/bin/read_manifest.rs
src/cargo/core/mod.rs
src/cargo/core/shell.rs
src/cargo/lib.rs
src/cargo/ops/cargo_install.rs
src/cargo/ops/cargo_new.rs
src/cargo/ops/cargo_rustc/job_queue.rs
src/cargo/ops/cargo_rustc/mod.rs
src/cargo/ops/registry.rs
src/cargo/util/config.rs
src/cargo/util/errors.rs
src/cargo/util/flock.rs
tests/cargotest/Cargo.toml
tests/cargotest/lib.rs
tests/new.rs
tests/search.rs
tests/shell.rs [deleted file]

index eb2efec09975e010e1feba363b8eced57811fe6f..01942aad08d872a17e180375f855883de5fb0bb8 100644 (file)
@@ -37,7 +37,7 @@ dependencies = [
  "shell-escape 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "tar 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "termcolor 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "toml 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -119,7 +119,6 @@ dependencies = [
  "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "tar 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -740,12 +739,11 @@ dependencies = [
 ]
 
 [[package]]
-name = "term"
-version = "0.4.5"
+name = "termcolor"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wincolor 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -866,6 +864,15 @@ name = "winapi-build"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "wincolor"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "ws2_32-sys"
 version = "0.2.1"
@@ -956,7 +963,7 @@ dependencies = [
 "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
 "checksum tar 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "281285b717926caa919ad905ef89c63d75805c7d89437fb873100925a53f2b1b"
 "checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
-"checksum term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d168af3930b369cfe245132550579d47dfd873d69470755a19c2c6568dbbd989"
+"checksum termcolor 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9a5193a56b8d82014662c4b933dea6bec851daf018a2b01722e007daaf5f9dca"
 "checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
 "checksum thread-id 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8df7875b676fddfadffd96deea3b1124e5ede707d4884248931077518cf1f773"
 "checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
@@ -974,4 +981,5 @@ dependencies = [
 "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
 "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
 "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
+"checksum wincolor 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "99c2af1426e2166e6f66d88b09b2a4d63afce06875f149174e386f2f1ee9779b"
 "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
index 26401fbd40ea022b84aece78bc1abbadb4f831be..d5b4b4254527c19fde1a16cef24891b56bea162c 100644 (file)
@@ -44,7 +44,7 @@ serde_json = "1.0"
 shell-escape = "0.1"
 tar = { version = "0.4", default-features = false }
 tempdir = "0.3"
-term = "0.4.4"
+termcolor = "0.3"
 toml = "0.4"
 url = "1.1"
 
index ae4409723d43974a17670c09d8cfea79aaf069b1..5e54973bfc194396fe39462771181e7f99471552 100644 (file)
@@ -15,7 +15,7 @@ use std::env;
 use std::fs;
 use std::path::{Path, PathBuf};
 
-use cargo::core::shell::{Verbosity, ColorConfig};
+use cargo::core::shell::{Shell, Verbosity};
 use cargo::util::{self, CliResult, lev_distance, Config, CargoResult, CargoError, CargoErrorKind};
 use cargo::util::CliError;
 
@@ -75,7 +75,7 @@ fn main() {
     let config = match Config::default() {
         Ok(cfg) => cfg,
         Err(e) => {
-            let mut shell = cargo::shell(Verbosity::Verbose, ColorConfig::Auto);
+            let mut shell = Shell::new();
             cargo::exit_with_error(e.into(), &mut shell)
         }
     };
index c97456757d55566b390a11817cdd9478154155d8..651174e87079b6718881a0258c41f3d1f0452d6c 100644 (file)
@@ -29,7 +29,7 @@ Options:
 pub fn execute(options: Options, config: &Config) -> CliResult {
     debug!("executing; cmd=cargo-read-manifest; args={:?}",
            env::args().collect::<Vec<_>>());
-    config.shell().set_color_config(options.flag_color.as_ref().map(|s| &s[..]))?;
+    config.shell().set_color_choice(options.flag_color.as_ref().map(|s| &s[..]))?;
 
     let root = find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())?;
 
index 2c966f681b41ff6949d00d7bf8810abfff27a643..c63e9f8d38fb57fea14abe27754df4cf43cc1e51 100644 (file)
@@ -6,7 +6,7 @@ pub use self::package_id::PackageId;
 pub use self::package_id_spec::PackageIdSpec;
 pub use self::registry::Registry;
 pub use self::resolver::Resolve;
-pub use self::shell::{Shell, MultiShell, ShellConfig, Verbosity, ColorConfig};
+pub use self::shell::{Shell, Verbosity};
 pub use self::source::{Source, SourceId, SourceMap, GitReference};
 pub use self::summary::Summary;
 pub use self::workspace::{Workspace, WorkspaceConfig};
index 7ec566ac77d39a4f96062accfca32440c19ebb36..02161ff249d508837b3d9fe2e5fedcc8cac76b24 100644 (file)
@@ -1,13 +1,8 @@
 use std::fmt;
 use std::io::prelude::*;
-use std::io;
 
-use term::color::{Color, BLACK, RED, GREEN, YELLOW};
-use term::{self, Terminal, TerminfoTerminal, color, Attr};
-
-use self::AdequateTerminal::{NoColor, Colored};
-use self::Verbosity::{Verbose, Quiet};
-use self::ColorConfig::{Auto, Always, Never};
+use termcolor::{self, StandardStream, Color, ColorSpec, WriteColor};
+use termcolor::Color::{Green, Red, Yellow};
 
 use util::errors::CargoResult;
 
@@ -18,120 +13,97 @@ pub enum Verbosity {
     Quiet
 }
 
-#[derive(Clone, Copy, PartialEq)]
-pub enum ColorConfig {
-    Auto,
-    Always,
-    Never
-}
-
-impl fmt::Display for ColorConfig {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match *self {
-            ColorConfig::Auto => "auto",
-            ColorConfig::Always => "always",
-            ColorConfig::Never => "never",
-        }.fmt(f)
-    }
-}
-
-#[derive(Clone, Copy)]
-pub struct ShellConfig {
-    pub color_config: ColorConfig,
-    pub tty: bool
-}
-
-enum AdequateTerminal {
-    NoColor(Box<Write + Send>),
-    Colored(Box<Terminal<Output=Box<Write + Send>> + Send>)
-}
-
 pub struct Shell {
-    terminal: AdequateTerminal,
-    config: ShellConfig,
+    err: StandardStream,
+    verbosity: Verbosity,
+    choice: ColorChoice,
 }
 
-pub struct MultiShell {
-    out: Shell,
-    err: Shell,
-    verbosity: Verbosity
+#[derive(PartialEq, Clone, Copy)]
+pub enum ColorChoice {
+    Always,
+    Never,
+    CargoAuto,
 }
 
-impl MultiShell {
-    pub fn new(out: Shell, err: Shell, verbosity: Verbosity) -> MultiShell {
-        MultiShell { out: out, err: err, verbosity: verbosity }
-    }
-
-    // Create a quiet, basic shell from supplied writers.
-    pub fn from_write(out: Box<Write + Send>, err: Box<Write + Send>) -> MultiShell {
-        let config = ShellConfig { color_config: ColorConfig::Never, tty: false };
-        let out = Shell { terminal: NoColor(out), config: config.clone() };
-        let err = Shell { terminal: NoColor(err), config: config };
-        MultiShell {
-            out: out,
-            err: err,
-            verbosity: Verbosity::Quiet,
+impl Shell {
+    pub fn new() -> Shell {
+        Shell {
+            err: StandardStream::stderr(ColorChoice::CargoAuto.to_termcolor_color_choice()),
+            verbosity: Verbosity::Verbose,
+            choice: ColorChoice::CargoAuto,
         }
     }
 
-    pub fn out(&mut self) -> &mut Shell {
-        &mut self.out
+    fn print(&mut self,
+             status: &fmt::Display,
+             message: &fmt::Display,
+             color: Color,
+             justified: bool) -> CargoResult<()> {
+        match self.verbosity {
+            Verbosity::Quiet => Ok(()),
+            _ => {
+                self.err.reset()?;
+                self.err.set_color(ColorSpec::new()
+                                        .set_bold(true)
+                                        .set_fg(Some(color)))?;
+                if justified {
+                    write!(self.err, "{:>12}", status)?;
+                } else {
+                    write!(self.err, "{}", status)?;
+                }
+                self.err.reset()?;
+                write!(self.err, " {}\n", message)?;
+                Ok(())
+            }
+        }
     }
 
-    pub fn err(&mut self) -> &mut Shell {
+    pub fn err(&mut self) -> &mut StandardStream {
         &mut self.err
     }
 
-    pub fn say<T: ToString>(&mut self, message: T, color: Color)
-                            -> CargoResult<()> {
-        match self.verbosity {
-            Quiet => Ok(()),
-            _ => self.out().say(message, color)
-        }
-    }
-
-    pub fn status_with_color<T, U>(&mut self, status: T, message: U, color: Color)
-                                   -> CargoResult<()>
+    pub fn status<T, U>(&mut self, status: T, message: U) -> CargoResult<()>
         where T: fmt::Display, U: fmt::Display
     {
-        match self.verbosity {
-            Quiet => Ok(()),
-            _ => self.err().say_status(status, message, color, true)
-        }
+        self.print(&status, &message, Green, true)
     }
 
-    pub fn status<T, U>(&mut self, status: T, message: U) -> CargoResult<()>
+    pub fn status_with_color<T, U>(&mut self,
+                                   status: T,
+                                   message: U,
+                                   color: Color) -> CargoResult<()>
         where T: fmt::Display, U: fmt::Display
     {
-        self.status_with_color(status, message, GREEN)
+        self.print(&status, &message, color, true)
     }
 
     pub fn verbose<F>(&mut self, mut callback: F) -> CargoResult<()>
-        where F: FnMut(&mut MultiShell) -> CargoResult<()>
+        where F: FnMut(&mut Shell) -> CargoResult<()>
     {
         match self.verbosity {
-            Verbose => callback(self),
+            Verbosity::Verbose => callback(self),
             _ => Ok(())
         }
     }
 
     pub fn concise<F>(&mut self, mut callback: F) -> CargoResult<()>
-        where F: FnMut(&mut MultiShell) -> CargoResult<()>
+        where F: FnMut(&mut Shell) -> CargoResult<()>
     {
         match self.verbosity {
-            Verbose => Ok(()),
+            Verbosity::Verbose => Ok(()),
             _ => callback(self)
         }
     }
 
     pub fn error<T: fmt::Display>(&mut self, message: T) -> CargoResult<()> {
-        self.err().say_status("error:", message, RED, false)
+        self.print(&"error:", &message, Red, false)
     }
 
     pub fn warn<T: fmt::Display>(&mut self, message: T) -> CargoResult<()> {
         match self.verbosity {
-            Quiet => Ok(()),
-            _ => self.err().say_status("warning:", message, YELLOW, false),
+            Verbosity::Quiet => Ok(()),
+            _ => self.print(&"warning:", &message, Yellow, false),
         }
     }
 
@@ -139,179 +111,57 @@ impl MultiShell {
         self.verbosity = verbosity;
     }
 
-    pub fn set_color_config(&mut self, color: Option<&str>) -> CargoResult<()> {
+    pub fn verbosity(&self) -> Verbosity {
+        self.verbosity
+    }
+
+    pub fn set_color_choice(&mut self, color: Option<&str>) -> CargoResult<()> {
         let cfg = match color {
-            Some("auto") => Auto,
-            Some("always") => Always,
-            Some("never") => Never,
+            Some("always") => ColorChoice::Always,
+            Some("never") => ColorChoice::Never,
 
-            None => Auto,
+            Some("auto") |
+            None => ColorChoice::CargoAuto,
 
             Some(arg) => bail!("argument for --color must be auto, always, or \
                                 never, but found `{}`", arg),
         };
-        self.out.set_color_config(cfg);
-        self.err.set_color_config(cfg);
-        Ok(())
-    }
-
-    pub fn get_verbose(&self) -> Verbosity {
-        self.verbosity
+        self.choice = cfg;
+        self.err = StandardStream::stderr(cfg.to_termcolor_color_choice());
+        return Ok(());
     }
 
-    pub fn color_config(&self) -> ColorConfig {
-        assert!(self.out.config.color_config == self.err.config.color_config);
-        self.out.config.color_config
+    pub fn color_choice(&self) -> ColorChoice {
+        self.choice
     }
 }
 
-impl Shell {
-    pub fn create<T: FnMut() -> Box<Write + Send>>(mut out_fn: T, config: ShellConfig) -> Shell {
-        let term = match Shell::get_term(out_fn()) {
-            Ok(t) => t,
-            Err(_) => NoColor(out_fn())
+impl ColorChoice {
+    fn to_termcolor_color_choice(&self) -> termcolor::ColorChoice {
+        return match *self {
+            ColorChoice::Always => termcolor::ColorChoice::Always,
+            ColorChoice::Never => termcolor::ColorChoice::Never,
+            ColorChoice::CargoAuto if isatty() => termcolor::ColorChoice::Auto,
+            ColorChoice::CargoAuto => termcolor::ColorChoice::Never,
         };
 
-        Shell {
-            terminal: term,
-            config: config,
-        }
-    }
-
-    #[cfg(any(windows))]
-    fn get_term(out: Box<Write + Send>) -> CargoResult<AdequateTerminal> {
-        // Check if the creation of a console will succeed
-        if ::term::WinConsole::new(vec![0u8; 0]).is_ok() {
-            let t = ::term::WinConsole::new(out)?;
-            if !t.supports_color() {
-                Ok(NoColor(Box::new(t)))
-            } else {
-                Ok(Colored(Box::new(t)))
-            }
-        } else {
-            // If we fail to get a windows console, we try to get a `TermInfo` one
-            Ok(Shell::get_terminfo_term(out))
-        }
-    }
-
-    #[cfg(any(unix))]
-    fn get_term(out: Box<Write + Send>) -> CargoResult<AdequateTerminal> {
-        Ok(Shell::get_terminfo_term(out))
-    }
-
-    fn get_terminfo_term(out: Box<Write + Send>) -> AdequateTerminal {
-        // Use `TermInfo::from_env()` and `TerminfoTerminal::supports_color()`
-        // to determine if creation of a TerminfoTerminal is possible regardless
-        // of the tty status. --color options are parsed after Shell creation so
-        // always try to create a terminal that supports color output. Fall back
-        // to a no-color terminal regardless of whether or not a tty is present
-        // and if color output is not possible.
-        match ::term::terminfo::TermInfo::from_env() {
-            Ok(ti) => {
-                let term = TerminfoTerminal::new_with_terminfo(out, ti);
-                if !term.supports_color() {
-                    NoColor(term.into_inner())
-                } else {
-                    // Color output is possible.
-                    Colored(Box::new(term))
-                }
-            },
-            Err(_) => NoColor(out),
-        }
-    }
-
-    pub fn set_color_config(&mut self, color_config: ColorConfig) {
-        self.config.color_config = color_config;
-    }
-
-    pub fn say<T: ToString>(&mut self, message: T, color: Color) -> CargoResult<()> {
-        self.reset()?;
-        if color != BLACK { self.fg(color)?; }
-        write!(self, "{}\n", message.to_string())?;
-        self.reset()?;
-        self.flush()?;
-        Ok(())
-    }
-
-    pub fn say_status<T, U>(&mut self,
-                            status: T,
-                            message: U,
-                            color: Color,
-                            justified: bool)
-                            -> CargoResult<()>
-        where T: fmt::Display, U: fmt::Display
-    {
-        self.reset()?;
-        if color != BLACK { self.fg(color)?; }
-        if self.supports_attr(Attr::Bold) { self.attr(Attr::Bold)?; }
-        if justified {
-            write!(self, "{:>12}", status.to_string())?;
-        } else {
-            write!(self, "{}", status)?;
-        }
-        self.reset()?;
-        write!(self, " {}\n", message)?;
-        self.flush()?;
-        Ok(())
-    }
-
-    fn fg(&mut self, color: color::Color) -> CargoResult<bool> {
-        let colored = self.colored();
-
-        match self.terminal {
-            Colored(ref mut c) if colored => c.fg(color)?,
-            _ => return Ok(false),
-        }
-        Ok(true)
-    }
-
-    fn attr(&mut self, attr: Attr) -> CargoResult<bool> {
-        let colored = self.colored();
-
-        match self.terminal {
-            Colored(ref mut c) if colored => c.attr(attr)?,
-            _ => return Ok(false)
-        }
-        Ok(true)
-    }
-
-    fn supports_attr(&self, attr: Attr) -> bool {
-        let colored = self.colored();
-
-        match self.terminal {
-            Colored(ref c) if colored => c.supports_attr(attr),
-            _ => false
-        }
-    }
-
-    fn reset(&mut self) -> term::Result<()> {
-        let colored = self.colored();
+        #[cfg(unix)]
+        fn isatty() -> bool {
+            extern crate libc;
 
-        match self.terminal {
-            Colored(ref mut c) if colored => c.reset()?,
-            _ => ()
+            unsafe { libc::isatty(libc::STDERR_FILENO) != 0 }
         }
-        Ok(())
-    }
-
-    fn colored(&self) -> bool {
-        self.config.tty && Auto == self.config.color_config
-            || Always == self.config.color_config
-    }
-}
 
-impl Write for Shell {
-    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        match self.terminal {
-            Colored(ref mut c) => c.write(buf),
-            NoColor(ref mut n) => n.write(buf)
-        }
-    }
+        #[cfg(windows)]
+        fn isatty() -> bool {
+            extern crate kernel32;
+            extern crate winapi;
 
-    fn flush(&mut self) -> io::Result<()> {
-        match self.terminal {
-            Colored(ref mut c) => c.flush(),
-            NoColor(ref mut n) => n.flush()
+            unsafe {
+                let handle = kernel32::GetStdHandle(winapi::STD_ERROR_HANDLE);
+                let mut out = 0;
+                kernel32::GetConsoleMode(handle, &mut out) != 0
+            }
         }
     }
 }
index 38ebe82ae520619b8778306a82e22a8560ef8db4..e2b39cc9244622d3bc696abfe2eef90eb8ce4bc1 100755 (executable)
@@ -28,11 +28,11 @@ extern crate serde_ignored;
 extern crate shell_escape;
 extern crate tar;
 extern crate tempdir;
-extern crate term;
+extern crate termcolor;
 extern crate toml;
 extern crate url;
 
-use std::io;
+use std::io::Write;
 use std::fmt;
 use std::error::Error;
 
@@ -41,9 +41,8 @@ use serde::Deserialize;
 use serde::ser;
 use docopt::Docopt;
 
-use core::{Shell, MultiShell, ShellConfig, Verbosity, ColorConfig};
-use core::shell::Verbosity::{Verbose};
-use term::color::{BLACK};
+use core::Shell;
+use core::shell::Verbosity::Verbose;
 
 pub use util::{CargoError, CargoErrorKind, CargoResult, CliError, CliResult, Config};
 
@@ -130,90 +129,46 @@ pub fn print_json<T: ser::Serialize>(obj: &T) {
     println!("{}", encoded);
 }
 
-pub fn shell(verbosity: Verbosity, color_config: ColorConfig) -> MultiShell {
-    enum Output {
-        Stdout,
-        Stderr,
-    }
-
-    let tty = isatty(Output::Stderr);
-
-    let config = ShellConfig { color_config: color_config, tty: tty };
-    let err = Shell::create(|| Box::new(io::stderr()), config);
-
-    let tty = isatty(Output::Stdout);
-
-    let config = ShellConfig { color_config: color_config, tty: tty };
-    let out = Shell::create(|| Box::new(io::stdout()), config);
-
-    return MultiShell::new(out, err, verbosity);
-
-    #[cfg(unix)]
-    fn isatty(output: Output) -> bool {
-        let fd = match output {
-            Output::Stdout => libc::STDOUT_FILENO,
-            Output::Stderr => libc::STDERR_FILENO,
-        };
-
-        unsafe { libc::isatty(fd) != 0 }
-    }
-    #[cfg(windows)]
-    fn isatty(output: Output) -> bool {
-        extern crate kernel32;
-        extern crate winapi;
-
-        let handle = match output {
-            Output::Stdout => winapi::winbase::STD_OUTPUT_HANDLE,
-            Output::Stderr => winapi::winbase::STD_ERROR_HANDLE,
-        };
-
-        unsafe {
-            let handle = kernel32::GetStdHandle(handle);
-            let mut out = 0;
-            kernel32::GetConsoleMode(handle, &mut out) != 0
-        }
-    }
-}
-
-pub fn exit_with_error(err: CliError, shell: &mut MultiShell) -> ! {
+pub fn exit_with_error(err: CliError, shell: &mut Shell) -> ! {
     debug!("exit_with_error; err={:?}", err);
 
     let CliError { error, exit_code, unknown } = err;
     // exit_code == 0 is non-fatal error, e.g. docopt version info
     let fatal = exit_code != 0;
 
-    let hide = unknown && shell.get_verbose() != Verbose;
+    let hide = unknown && shell.verbosity() != Verbose;
 
     if let Some(error) = error {
-        let _ignored_result = if hide {
-            shell.error("An unknown error occurred")
+        if hide {
+            drop(shell.error("An unknown error occurred"))
         } else if fatal {
-            shell.error(&error)
+            drop(shell.error(&error))
         } else {
-            shell.say(&error, BLACK)
-        };
+            drop(writeln!(shell.err(), "{}", error))
+        }
 
         if !handle_cause(error, shell) || hide {
-            let _ = shell.err().say("\nTo learn more, run the command again \
-                                     with --verbose.".to_string(), BLACK);
+            drop(writeln!(shell.err(), "\nTo learn more, run the command again \
+                                        with --verbose."));
         }
     }
 
     std::process::exit(exit_code)
 }
 
-pub fn handle_error(err: CargoError, shell: &mut MultiShell) {
+pub fn handle_error(err: CargoError, shell: &mut Shell) {
     debug!("handle_error; err={:?}", &err);
 
     let _ignored_result = shell.error(&err);
     handle_cause(err, shell);
 }
 
-fn handle_cause<E, EKind>(cargo_err: E, shell: &mut MultiShell) -> bool
-    where E: ChainedError<ErrorKind=EKind> + 'static {
-    fn print(error: String, shell: &mut MultiShell) {
-        let _ = shell.err().say("\nCaused by:", BLACK);
-        let _ = shell.err().say(format!("  {}", error), BLACK);
+fn handle_cause<E, EKind>(cargo_err: E, shell: &mut Shell) -> bool
+    where E: ChainedError<ErrorKind=EKind> + 'static
+{
+    fn print(error: String, shell: &mut Shell) {
+        drop(writeln!(shell.err(), "\nCaused by:"));
+        drop(writeln!(shell.err(), "  {}", error));
     }
 
     //Error inspection in non-verbose mode requires inspecting the
@@ -228,7 +183,7 @@ fn handle_cause<E, EKind>(cargo_err: E, shell: &mut MultiShell) -> bool
         std::mem::transmute::<&Error, &Error>(r)
     }
 
-    let verbose = shell.get_verbose();
+    let verbose = shell.verbosity();
 
     if verbose == Verbose {
         //The first error has already been printed to the shell
index ceb67c323c7624079c4988225a5a9cc4c7ce7735..5421ebbcee3d9daa520e2b92a7e55a19f36b1a7b 100644 (file)
@@ -460,12 +460,10 @@ pub fn install_list(dst: Option<&str>, config: &Config) -> CargoResult<()> {
     let dst = resolve_root(dst, config)?;
     let dst = metadata(config, &dst)?;
     let list = read_crate_list(dst.file())?;
-    let mut shell = config.shell();
-    let out = shell.out();
     for (k, v) in list.v1.iter() {
-        writeln!(out, "{}:", k)?;
+        println!("{}:", k);
         for bin in v {
-            writeln!(out, "    {}", bin)?;
+            println!("    {}", bin);
         }
     }
     Ok(())
index ad049e494e486802e377ea952e1d68433475194d..3ac323905d492467d05dbcd976183db0d438502b 100644 (file)
@@ -1,7 +1,8 @@
+use std::collections::BTreeMap;
 use std::env;
 use std::fs;
+use std::io::Write;
 use std::path::Path;
-use std::collections::BTreeMap;
 
 use serde::{Deserialize, Deserializer};
 use serde::de;
@@ -9,8 +10,6 @@ use serde::de;
 use git2::Config as GitConfig;
 use git2::Repository as GitRepository;
 
-use term::color::BLACK;
-
 use core::Workspace;
 use ops::is_bad_artifact_name;
 use util::{GitRepo, HgRepo, PijulRepo, internal};
@@ -111,10 +110,9 @@ fn get_name<'a>(path: &'a Path, opts: &'a NewOptions, config: &Config) -> CargoR
     } else {
         let new_name = strip_rust_affixes(dir_name);
         if new_name != dir_name {
-            let message = format!(
-                "note: package will be named `{}`; use --name to override",
-                new_name);
-            config.shell().say(&message, BLACK)?;
+            writeln!(config.shell().err(),
+                     "note: package will be named `{}`; use --name to override",
+                     new_name)?;
         }
         Ok(new_name)
     }
index fa008289ddaeb7c86239dcc5641aaf509f161c62..aec3e3c853c412160d4ae3a0bcf171e31bae763b 100644 (file)
@@ -7,7 +7,6 @@ use std::sync::mpsc::{channel, Sender, Receiver};
 
 use crossbeam::{self, Scope};
 use jobserver::{Acquired, HelperThread};
-use term::color::YELLOW;
 
 use core::{PackageId, Target, Profile};
 use util::{Config, DependencyQueue, Fresh, Dirty, Freshness};
@@ -212,7 +211,7 @@ impl<'a> JobQueue<'a> {
                 }
                 Message::Stdout(out) => {
                     if cx.config.extra_verbose() {
-                        writeln!(cx.config.shell().out(), "{}", out)?;
+                        println!("{}", out);
                     }
                 }
                 Message::Stderr(err) => {
@@ -236,9 +235,9 @@ impl<'a> JobQueue<'a> {
                             if self.active > 0 {
                                 error = Some("build failed".into());
                                 handle_error(e, &mut *cx.config.shell());
-                                cx.config.shell().say(
-                                            "Build failed, waiting for other \
-                                             jobs to finish...", YELLOW)?;
+                                cx.config.shell().warn(
+                                            "build failed, waiting for other \
+                                             jobs to finish...")?;
                             }
                             else {
                                 error = Some(e);
index e77740763024a1a37d0eb3feacc30359137fb2ee..67bb834a271ab4372968247d05dc61105dca3960 100644 (file)
@@ -10,7 +10,7 @@ use serde_json;
 
 use core::{Package, PackageId, PackageSet, Target, Resolve};
 use core::{Profile, Profiles, Workspace};
-use core::shell::ColorConfig;
+use core::shell::ColorChoice;
 use util::{self, ProcessBuilder, machine_message};
 use util::{Config, internal, profile, join_paths, short_hash};
 use util::errors::{CargoResult, CargoResultExt};
@@ -679,9 +679,10 @@ fn build_base_args(cx: &mut Context,
 
     cmd.arg(&root_path(cx, unit));
 
-    let color_config = cx.config.shell().color_config();
-    if color_config != ColorConfig::Auto {
-        cmd.arg("--color").arg(&color_config.to_string());
+    match cx.config.shell().color_choice() {
+        ColorChoice::Always => { cmd.arg("--color").arg("always"); }
+        ColorChoice::Never => { cmd.arg("--color").arg("never"); }
+        ColorChoice::CargoAuto => {}
     }
 
     if cx.build_config.json_messages {
index 78681a6904e8a0703feea803fb4c3cb4271cf835..01a5e0710775c121bc01948dba4eb5cf45957d5a 100644 (file)
@@ -1,12 +1,12 @@
 use std::env;
 use std::fs::{self, File};
+use std::io::Write;
 use std::iter::repeat;
 use std::time::Duration;
 
 use curl::easy::{Easy, SslOpt};
 use git2;
 use registry::{Registry, NewCrate, NewCrateDependency};
-use term::color::BLACK;
 
 use url::percent_encoding::{percent_encode, QUERY_ENCODE_SET};
 
@@ -424,25 +424,19 @@ pub fn search(query: &str,
             }
             None => name
         };
-        config.shell().say(line, BLACK)?;
+        writeln!(config.shell().err(), "{}", line)?;
     }
 
     let search_max_limit = 100;
     if total_crates > limit as u32 && limit < search_max_limit {
-        config.shell().say(
-            format!("... and {} crates more (use --limit N to see more)",
-                    total_crates - limit as u32),
-            BLACK)
-        ?;
+        writeln!(config.shell().err(),
+                 "... and {} crates more (use --limit N to see more)",
+                 total_crates - limit as u32)?;
     } else if total_crates > limit as u32 && limit >= search_max_limit {
-        config.shell().say(
-            format!(
-                "... and {} crates more (go to http://crates.io/search?q={} to see more)",
-                total_crates - limit as u32,
-                percent_encode(query.as_bytes(), QUERY_ENCODE_SET)
-            ),
-            BLACK)
-        ?;
+        writeln!(config.shell().err(),
+                 "... and {} crates more (go to http://crates.io/search?q={} to see more)",
+                 total_crates - limit as u32,
+                 percent_encode(query.as_bytes(), QUERY_ENCODE_SET))?;
     }
 
     Ok(())
index 32976225b76fcec241afc0a8e28bc65ebf51a476..5edeeef96374971692401e6d2cf99ac58e77bf61 100644 (file)
@@ -12,8 +12,8 @@ use std::path::{Path, PathBuf};
 use std::str::FromStr;
 use std::sync::{Once, ONCE_INIT};
 
-use core::MultiShell;
-use core::shell::{Verbosity, ColorConfig};
+use core::Shell;
+use core::shell::Verbosity;
 use jobserver;
 use serde::{Serialize, Serializer};
 use toml;
@@ -28,7 +28,7 @@ use self::ConfigValue as CV;
 
 pub struct Config {
     home_path: Filesystem,
-    shell: RefCell<MultiShell>,
+    shell: RefCell<Shell>,
     rustc: LazyCell<Rustc>,
     values: LazyCell<HashMap<String, ConfigValue>>,
     cwd: PathBuf,
@@ -41,7 +41,7 @@ pub struct Config {
 }
 
 impl Config {
-    pub fn new(shell: MultiShell,
+    pub fn new(shell: Shell,
                cwd: PathBuf,
                homedir: PathBuf) -> Config {
         static mut GLOBAL_JOBSERVER: *mut jobserver::Client = 0 as *mut _;
@@ -77,7 +77,7 @@ impl Config {
     }
 
     pub fn default() -> CargoResult<Config> {
-        let shell = ::shell(Verbosity::Verbose, ColorConfig::Auto);
+        let shell = Shell::new();
         let cwd = env::current_dir().chain_err(|| {
             "couldn't get the current directory of the process"
         })?;
@@ -106,7 +106,7 @@ impl Config {
         self.home_path.join("registry").join("src")
     }
 
-    pub fn shell(&self) -> RefMut<MultiShell> {
+    pub fn shell(&self) -> RefMut<Shell> {
         self.shell.borrow_mut()
     }
 
@@ -405,7 +405,7 @@ impl Config {
         };
 
         self.shell().set_verbosity(verbosity);
-        self.shell().set_color_config(color.map(|s| &s[..]))?;
+        self.shell().set_color_choice(color.map(|s| &s[..]))?;
         self.extra_verbose.set(extra_verbose);
         self.frozen.set(frozen);
         self.locked.set(locked);
index 11d6dfdc6bf4c456d2fc2b22f1f0c571a68815c6..7b51c527d7ec00da74c0d7d753289cc0cf132246 100644 (file)
@@ -12,7 +12,6 @@ use curl;
 use git2;
 use semver;
 use serde_json;
-use term;
 use toml;
 use registry;
 
@@ -32,7 +31,6 @@ error_chain! {
         SerdeJson(serde_json::Error);
         TomlSer(toml::ser::Error);
         TomlDe(toml::de::Error);
-        Term(term::Error);
         ParseInt(num::ParseIntError);
         ParseBool(str::ParseBoolError);
         Parse(string::ParseError);
@@ -78,7 +76,6 @@ impl CargoError {
             &CargoErrorKind::Semver(_) |
             &CargoErrorKind::Io(_) |
             &CargoErrorKind::SerdeJson(_) |
-            &CargoErrorKind::Term(_) |
             &CargoErrorKind::ParseInt(_) |
             &CargoErrorKind::ParseBool(_) |
             &CargoErrorKind::Parse(_) |
index 505a88a008a885f54381f13b8df62bc49c87edb3..4cdcf5f9043c73bb9a7c615121af90c8329228a5 100644 (file)
@@ -3,7 +3,7 @@ use std::io::*;
 use std::io;
 use std::path::{Path, PathBuf, Display};
 
-use term::color::CYAN;
+use termcolor::Color::Cyan;
 use fs2::{FileExt, lock_contended_error};
 #[allow(unused_imports)]
 use libc;
@@ -290,7 +290,7 @@ fn acquire(config: &Config,
         }
     }
     let msg = format!("waiting for file lock on {}", msg);
-    config.shell().status_with_color("Blocking", &msg, CYAN)?;
+    config.shell().status_with_color("Blocking", &msg, Cyan)?;
 
     return block().chain_err(|| {
         format!("failed to lock file: {}", path.display())
index f5b1cc8ca3024ede61554d3e48462e88ccdd2d13..dea88263844089054e9d6154a9ffcfd4d118c2bd 100644 (file)
@@ -21,6 +21,5 @@ serde = "1.0"
 serde_json = "1.0"
 tar = { version = "0.4", default-features = false }
 tempdir = "0.3"
-term = "0.4.4"
 url = "1.1"
 winapi = "0.2"
index 919768c18871ddd8892f1b37a4af950837e16642..dcb098a8e461ae7ac5eb18da2ebf90ba4333e524 100644 (file)
@@ -13,7 +13,6 @@ extern crate serde;
 extern crate serde_json;
 extern crate tar;
 extern crate tempdir;
-extern crate term;
 extern crate url;
 #[cfg(windows)] extern crate kernel32;
 #[cfg(windows)] extern crate winapi;
index 8ba3150566afa09c83925b4bf9335e59c3d92c84..fa75d2da870aa9edee3bbf9b64bff2fb354d7851 100644 (file)
@@ -155,7 +155,7 @@ use --name to override crate name"));
 fn rust_prefix_stripped() {
     assert_that(cargo_process("new").arg("--lib").arg("rust-foo").env("USER", "foo"),
                 execs().with_status(0)
-                       .with_stdout("note: package will be named `foo`; use --name to override"));
+                       .with_stderr_contains("note: package will be named `foo`; use --name to override"));
     let toml = paths::root().join("rust-foo/Cargo.toml");
     let mut contents = String::new();
     File::open(&toml).unwrap().read_to_string(&mut contents).unwrap();
index 4c55058ade51e96195049847527b80c1cbbc782e..ff224fe95216c4be04c23e7ca2869a133d87000d 100644 (file)
@@ -84,9 +84,7 @@ fn simple() {
     assert_that(cargo_process("search").arg("postgres")
                     .arg("--host").arg(registry().to_string()),
                 execs().with_status(0)
-                       .with_stderr("\
-[UPDATING] registry `[..]`")
-                       .with_stdout("\
+                       .with_stderr_contains("\
 hoare = \"0.1.1\"    # Design by contract style assertions for Rust"));
 }
 
@@ -136,9 +134,7 @@ fn multiple_query_params() {
     assert_that(cargo_process("search").arg("postgres").arg("sql")
                     .arg("--host").arg(registry().to_string()),
                 execs().with_status(0)
-                       .with_stderr("\
-[UPDATING] registry `[..]`")
-                       .with_stdout("\
+                       .with_stderr_contains("\
 hoare = \"0.1.1\"    # Design by contract style assertions for Rust"));
 }
 
diff --git a/tests/shell.rs b/tests/shell.rs
deleted file mode 100644 (file)
index 8dc0786..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-extern crate cargo;
-extern crate cargotest;
-extern crate hamcrest;
-extern crate term;
-
-use std::io::prelude::*;
-use std::io;
-use std::sync::{Arc, Mutex};
-
-use cargo::core::shell::ColorConfig::{Auto,Always, Never};
-use cargo::core::shell::{Shell, ShellConfig};
-use cargo::util::CargoResult;
-use cargotest::support::{Tap, execs, shell_writes};
-use hamcrest::{assert_that};
-use term::{Terminal, TerminfoTerminal, color};
-
-struct Sink(Arc<Mutex<Vec<u8>>>);
-
-impl Write for Sink {
-    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
-        Write::write(&mut *self.0.lock().unwrap(), data)
-    }
-    fn flush(&mut self) -> io::Result<()> { Ok(()) }
-}
-
-#[test]
-fn non_tty() {
-    let config = ShellConfig { color_config: Auto, tty: false };
-    let a = Arc::new(Mutex::new(Vec::new()));
-
-    Shell::create(|| Box::new(Sink(a.clone())), config).tap(|shell| {
-        shell.say("Hey Alex", color::RED).unwrap();
-    });
-    let buf = a.lock().unwrap().clone();
-    assert_that(&buf[..], shell_writes("Hey Alex\n"));
-}
-
-#[test]
-fn color_explicitly_disabled() {
-    let term = TerminfoTerminal::new(Vec::new());
-    if term.is_none() { return }
-
-    let config = ShellConfig { color_config: Never, tty: true };
-    let a = Arc::new(Mutex::new(Vec::new()));
-
-    Shell::create(|| Box::new(Sink(a.clone())), config).tap(|shell| {
-        shell.say("Hey Alex", color::RED).unwrap();
-    });
-    let buf = a.lock().unwrap().clone();
-    assert_that(&buf[..], shell_writes("Hey Alex\n"));
-}
-
-#[test]
-fn colored_shell() {
-    let term = TerminfoTerminal::new(Vec::new());
-    if term.is_none() { return }
-
-    let config = ShellConfig { color_config: Auto, tty: true };
-    let a = Arc::new(Mutex::new(Vec::new()));
-
-    Shell::create(|| Box::new(Sink(a.clone())), config).tap(|shell| {
-        shell.say("Hey Alex", color::RED).unwrap();
-    });
-    let buf = a.lock().unwrap().clone();
-    let expected_output = if term.unwrap().supports_color() {
-        shell_writes(colored_output("Hey Alex\n", color::RED).unwrap())
-    } else {
-        shell_writes("Hey Alex\n")
-    };
-    assert_that(&buf[..], expected_output);
-}
-
-#[test]
-fn color_explicitly_enabled() {
-    let term = TerminfoTerminal::new(Vec::new());
-    if term.is_none() { return }
-    if !term.unwrap().supports_color() { return }
-
-    let config = ShellConfig { color_config: Always, tty: false };
-    let a = Arc::new(Mutex::new(Vec::new()));
-
-    Shell::create(|| Box::new(Sink(a.clone())), config).tap(|shell| {
-        shell.say("Hey Alex", color::RED).unwrap();
-    });
-    let buf = a.lock().unwrap().clone();
-    assert_that(&buf[..],
-                shell_writes(colored_output("Hey Alex\n",
-                                            color::RED).unwrap()));
-}
-
-#[test]
-fn no_term() {
-    // Verify that shell creation is successful when $TERM does not exist.
-    assert_that(cargotest::cargo_process().env_remove("TERM"),
-                execs().with_stderr(""));
-}
-
-fn colored_output(string: &str, color: color::Color) -> CargoResult<String> {
-    let mut term = TerminfoTerminal::new(Vec::new()).unwrap();
-    term.reset()?;
-    term.fg(color)?;
-    write!(&mut term, "{}", string)?;
-    term.reset()?;
-    term.flush()?;
-    Ok(String::from_utf8_lossy(term.get_ref()).to_string())
-}